/*
 *   This program is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

/*
 *    Environment.java
 *    Copyright (C) 2008-2012 University of Waikato, Hamilton, New Zealand
 *
 */

package weka.core;

import java.util.Enumeration;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.TreeMap;

/**
 * This class encapsulates a map of all environment and java system properties.
 * There are methods for adding and removing variables to this Environment
 * object as well as to the system wide global environment. There is also a
 * method for replacing key names (enclosed by ${}) with their associated value
 * in Strings.
 * 
 * @author Mark Hall (mhall{[at]}pentaho{[dot]}com)
 * @version $Revision$
 */
public class Environment {

    private static Environment m_systemWide = new Environment();

    // Map to hold all the system environment variables + java properties
    private final Map<String, String> m_envVars = new TreeMap<String, String>();

    /**
     * Constructs a new Environment object with all environment variables and java
     * properties set.
     */
    public Environment() {
        // get the env variables first
        Map<String, String> env = System.getenv();
        Set<String> keys = env.keySet();
        Iterator<String> i = keys.iterator();
        while (i.hasNext()) {
            String kv = i.next();
            String value = env.get(kv);
            m_envVars.put(kv, value);
        }

        // get the java properties
        Properties jvmProps = System.getProperties();
        Enumeration<?> pKeys = jvmProps.propertyNames();
        while (pKeys.hasMoreElements()) {
            String kv = (String) pKeys.nextElement();
            String value = jvmProps.getProperty(kv);
            m_envVars.put(kv, value);
        }
        m_envVars.put("weka.version", Version.VERSION);
    }

    /**
     * Constructor that makes a new Environment object containing all the entries in
     * the supplied one
     *
     * @param other the Environment object to copy to this one
     */
    public Environment(Environment other) {
        m_envVars.putAll(other.m_envVars);
    }

    /**
     * Get the singleton system-wide (visible to every class in the running VM) set
     * of environment variables.
     * 
     * @return the system-wide set of environment variables.
     */
    public static Environment getSystemWide() {
        return m_systemWide;
    }

    /**
     * Tests for the presence of environment variables.
     * 
     * @param source the string to test
     * @return true if the argument contains one or more environment variables
     */
    public static boolean containsEnvVariables(String source) {
        return (source.indexOf("${") >= 0);
    }

    /**
     * Substitute a variable names for their values in the given string.
     * 
     * @param source the source string to replace variables in
     * @return a String with all variable names replaced with their values
     * @throws Exception if an unknown variable name is encountered
     */
    public String substitute(String source) throws Exception {
        // Grab each variable out of the string
        int index = source.indexOf("${");

        while (index >= 0) {
            index += 2;
            int endIndex = source.indexOf('}');
            if (endIndex >= 0 && endIndex > index + 1) {
                String key = source.substring(index, endIndex);

                // look this sucker up
                String replace = m_envVars.get(key);
                if (replace != null) {
                    String toReplace = "${" + key + "}";
                    source = source.replace(toReplace, replace);
                } else {
                    throw new Exception("[Environment] Variable " + key + " doesn't seem to be set.");
                }
            } else {
                break;
            }
            index = source.indexOf("${");
        }
        return source;
    }

    /**
     * Add a variable to the internal map of this properties object.
     * 
     * @param key   the name of the variable
     * @param value its value
     */
    public void addVariable(String key, String value) {
        m_envVars.put(key, value);
    }

    /**
     * Add a a variable to the internal map of this properties object and to the
     * global system-wide environment;
     * 
     * @param key   the name of the variable
     * @param value its value
     */
    public void addVariableSystemWide(String key, String value) {
        addVariable(key, value); // local

        // system wide
        if (this != getSystemWide()) {
            getSystemWide().addVariableSystemWide(key, value);
        }
        System.setProperty(key, value);
    }

    /**
     * Remove a named variable from the map.
     * 
     * @param key the name of the varaible to remove.
     */
    public void removeVariable(String key) {
        m_envVars.remove(key);
    }

    /**
     * Get the names of the variables (keys) stored in the internal map.
     * 
     * @return a Set of variable names (keys)
     */
    public Set<String> getVariableNames() {
        return m_envVars.keySet();
    }

    /**
     * Get the value for a particular variable.
     * 
     * @param key the name of the variable to get
     * @return the associated value or null if this variable is not in the internal
     *         map
     */
    public String getVariableValue(String key) {
        return m_envVars.get(key);
    }

    /**
     * Main method for testing this class.
     * 
     * @param args a list of strings to replace variables in (e.g. "\${os.name}
     *             "\${java.version}")
     */
    public static void main(String[] args) {
        Environment t = new Environment();
        // String test =
        // "Here is a string with the variable ${java.version} and ${os.name} in it";

        if (args.length == 0) {
            System.err.println("Usage: java weka.core.Environment <string> <string> ...");
        } else {
            try {
                for (String arg : args) {
                    String newS = t.substitute(arg);
                    System.out.println("Original string:\n" + arg + "\n\nNew string:\n" + newS);
                }
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }
    }

}
